package com.berchen.juc;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 可重入锁：可重复可递归的锁，在外层使用锁之后，在内层任然可以使用，并且不发生死锁，这样的锁就叫做可重入锁。
 *
 * 在一个synchronized修饰的方法或代码块内部，调用本类的其他synchronized修饰的方法或代码块时，是永远可以得到锁的。
 */
public class ReEnterLockDemo {

    // 作为一个锁
    static Object objectLockA =new Object();

    public static void m1(){
        new Thread(()->{
            synchronized (objectLockA){ // synchronized 获取到 锁
                System.out.println(Thread.currentThread().getName()+"\t ----- 外层调用");
                synchronized (objectLockA){ //synchronized 获取到同一把锁
                    System.out.println(Thread.currentThread().getName()+"\t ----- 中层调用");
                    synchronized (objectLockA){// synchronized继续获取
                        System.out.println(Thread.currentThread().getName()+"\t ----- 内层调用");

                    }
                }
            }
        },"t1").start();
    }

    /**
     * 下面三个静态同步方法 的锁都是同一个 本类.class
     */
    public static synchronized void m2(){
        System.out.println("==== 外面");
        m3();
    }
    public static synchronized void m3(){
        System.out.println("==== 中");
        m4();
    }
    public static synchronized void m4(){
        System.out.println("==== 外面");
    }

    public static void main(String[] args){
        m1();
        m2();
        m5();
    }


    static Lock lock=new ReentrantLock();

    public static void m5(){
        new Thread(()->{
            try{
                lock.lock();    // 加外部锁（下面三个锁都是同一个，该线程的锁）
                System.out.println(Thread.currentThread().getName()+"\t ----- 外层调用");
                try{
                    lock.lock(); // 加中部锁
                    System.out.println(Thread.currentThread().getName()+"\t ----- 内层调用");
                    try{
                        lock.lock(); // 加内部锁
                        System.out.println(Thread.currentThread().getName()+"\t ----- 内层调用");
                    }finally {

                        /**
                         * 加锁次数和解锁次数，必须相同，否则其他线程获取不到锁的情况就会发生。
                         */
                        lock.unlock();
                    }
                }finally {
                    lock.unlock();
                }
            }finally {
                lock.unlock();
            }
        },"t2").start();

    }
}
